<?php
/**
 * @author : lihui
 * @email  : lihui@mama.com
 * @time   : 2022/9/7 17:42
 *
 * 精彩练习1
 * 基于给定的宠物类、猫类和狗类，实现猫狗队列，要求：
 * 1. 不能改变已给出类
 * 2. 可以单独给猫出队
 * 3. 可以单独给狗出队
 * 4. 给宠物出队
 *
 * 思路：
 * 1. 在不破坏猫类、狗类基础上，实现一个猫狗类，里面有一个计数器，表示猫实例、狗实例的顺序编号
 * 2. 一个队列放猫实例 一个队列放狗实例
 * 计数器从1开始递增，队头的编号一定是最小的，两个队列中最小编号的队头就是该宠物队列的队头
 */

namespace Xxb\XxbPhpAlgo\Topic;

use Xxb\XxbPhpAlgo\ArrayQueue;
use Xxb\XxbPhpAlgo\Contract\XxbQueueInterface;

class CatDogQueue implements XxbQueueInterface
{
    /**
     * @var ArrayQueue 猫队列
     */
    private $catQueue;

    /**
     * @var ArrayQueue 狗队列
     */
    private $dogQueue;

    /**
     * @var int 编号
     */
    private static $counter;

    private $size;

    public function __construct()
    {
        $this->catQueue = new ArrayQueue();
        $this->dogQueue = new ArrayQueue();
        self::$counter = 0;
        $this->size = 0;
    }

    public function push($value)
    {
        if (!($value instanceof Pet)) {
            throw new \TypeError('enqueue error, illegal pet.');
        }

        $combineObj = new CatDog($value, ++self::$counter);

        if ($value instanceof Cat) {
            $this->catQueue->push($combineObj);
        } elseif ($value instanceof Dog) {
            $this->dogQueue->push($combineObj);
        } else {
            throw new \TypeError('enqueue error, only cat or dog.');
        }

        $this->size++;
    }

    /**
     * @return Pet
     */
    public function shift()
    {
        if ($this->isEmpty()) {
            throw new \RuntimeException('dequeue error, empty queue.');
        }

        /**
         * @var $obj CatDog
         */
        $obj = null;
        if ($this->catQueue->isEmpty()) {
            // 猫队列是空 即只有狗入队了
            $obj = $this->dogQueue->shift();
        } elseif ($this->dogQueue->isEmpty()) {
            $obj = $this->catQueue->shift();
        } else {
            // 猫、狗都入队了
            /**
             * @var $dog CatDog
             */
            $dog = $this->dogQueue->bottom();
            /**
             * @var $cat CatDog
             */
            $cat = $this->catQueue->bottom();

            if ($dog->num < $cat->num) {
                $obj = $this->dogQueue->shift();
            } else {
                $obj = $this->catQueue->shift();
            }
        }
        $this->size--;
        return $obj->getPet();
    }

    /**
     * @return Cat
     */
    public function shiftCat()
    {
        if ($this->catQueue->isEmpty()) {
            throw new \RuntimeException('dequeue error, empty cat queue.');
        }
        /**
         * @var $obj CatDog
         */
        $obj = $this->catQueue->shift();
        $this->size--;
        return $obj->getPet();
    }

    /**
     * @return Dog
     */
    public function shiftDog()
    {
        if ($this->dogQueue->isEmpty()) {
            throw new \RuntimeException('dequeue error, empty dog queue.');
        }
        /**
         * @var $obj CatDog
         */
        $obj = $this->dogQueue->shift();
        $this->size--;
        return $obj->getPet();
    }

    public function top()
    {
        // TODO: Implement top() method.
    }

    public function bottom()
    {
        // TODO: Implement bottom() method.
    }

    public function isEmpty()
    {
        return $this->size == 0;
    }

    public function getSize()
    {
        return $this->size;
    }
}
